package com.jerry.secondkill.controller;

import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jerry.secondkill.access.AccessLimit;
import com.jerry.secondkill.domain.SeckillOrder;
import com.jerry.secondkill.domain.User;
import com.jerry.secondkill.rabbitmq.MQSender;
import com.jerry.secondkill.rabbitmq.SeckillMessage;
import com.jerry.secondkill.redis.RedisService;
import com.jerry.secondkill.redis.key.GoodsKey;
import com.jerry.secondkill.redis.key.OrderKey;
import com.jerry.secondkill.redis.key.SeckillKey;
import com.jerry.secondkill.result.CodeMsg;
import com.jerry.secondkill.result.Result;
import com.jerry.secondkill.service.GoodsService;
import com.jerry.secondkill.service.OrderService;
import com.jerry.secondkill.service.SeckillService;
import com.jerry.secondkill.service.UserService;
import com.jerry.secondkill.vo.GoodsVo;

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

	@Autowired
	UserService userService;
	@Autowired
	RedisService redisService;
	@Autowired
	GoodsService goodsService;
	@Autowired
	OrderService orderService;
	@Autowired
	SeckillService seckillService;
	
	@Autowired
	MQSender mqSender;
	
	private HashMap<Long, Boolean> localOverMap =  new HashMap<Long, Boolean>();
	
	/**
	 * 系统初始化时，把秒杀商品库存放到redis中
	 * */
	public void afterPropertiesSet() throws Exception {
		//获取所有秒杀商品
		List<GoodsVo> goodsList = goodsService.listGoodsVo();
		if(goodsList == null) {
			return;
		}
		for(GoodsVo goods : goodsList) {
			//秒杀商品<id,数量>放到redis中
			redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), goods.getStockCount());
			localOverMap.put(goods.getId(), false);
		}
	}
	
	@RequestMapping(value="/{path}/doSeckill", method=RequestMethod.POST)
	@ResponseBody
    public Result<Integer> doSeckill(
    		Model model,
    		User user, 
    		@RequestParam("goodsId")long goodsId,
    		@PathVariable("path") String path) {
    	model.addAttribute("user", user);
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	
    	//验证path
    	boolean check = seckillService.checkPath(user, goodsId, path);
    	if(!check){
    		return Result.error(CodeMsg.REQUEST_ILLEGAL);
    	}
    	
    	//内存中查看商品是否已完，目的：减少redis访问
    	boolean over = localOverMap.get(goodsId);
    	if(over) {
    		return Result.error(CodeMsg.SECKILL_OVER);
    	}
    	//在redis中预减库存
    	long stock = redisService.decr(GoodsKey.getSeckillGoodsStock, ""+goodsId);
    	if(stock < 0) {
    		localOverMap.put(goodsId, true);
    		return Result.error(CodeMsg.SECKILL_OVER);
    	}
    	//判断是否已经秒杀到了
    	SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(user.getId(), goodsId);
    	if(order != null) {
    		return Result.error(CodeMsg.REPEATE_SECKILL);
    	}
    	//入队
    	SeckillMessage mm = new SeckillMessage();
    	mm.setUser(user);
    	mm.setGoodsId(goodsId);
    	mqSender.sendSeckillMessage(mm);
    	
    	return Result.success(0);//返回排队中，0代表排队中
    }
	
	/**
     * orderId：成功
     * -1：秒杀失败
     * 0： 排队中
     * */
    @RequestMapping(value="/result", method=RequestMethod.GET)
    @ResponseBody
    public Result<Long> seckillResult(Model model,User user,
    		@RequestParam("goodsId")long goodsId) {
    	model.addAttribute("user", user);
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	long result  = seckillService.getSeckillResult(user.getId(), goodsId);
    	return Result.success(result);
    }
    
    @RequestMapping(value="/reset", method=RequestMethod.GET)
    @ResponseBody
    public Result<Boolean> reset(Model model) {
		List<GoodsVo> goodsList = goodsService.listGoodsVo();
		for(GoodsVo goods : goodsList) {
			goods.setStockCount(10);
			redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), 10);
			localOverMap.put(goods.getId(), false);
		}
		redisService.delete(OrderKey.getSeckillOrderByUidGid);
		redisService.delete(SeckillKey.isGoodsOver);
		seckillService.reset(goodsList);
		return Result.success(true);
	}
    
    @AccessLimit(seconds=5, maxCount=5, needLogin=true)
    @RequestMapping(value="/path", method=RequestMethod.GET)
    @ResponseBody
    public Result<String> getSeckillPath(HttpServletRequest request, User user,
    		@RequestParam("goodsId")long goodsId,
    		@RequestParam(value="verifyCode", defaultValue="0")int verifyCode
    		) {
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	//检测验证码的正确性
    	boolean check = seckillService.checkVerifyCode(user, goodsId, verifyCode);
    	if(!check) {
    		return Result.error(CodeMsg.REQUEST_ILLEGAL);
    	}
    	//获取秒杀地址
    	String path  = seckillService.createSeckillPath(user, goodsId);
    	return Result.success(path);
    }
    
    
    @RequestMapping(value="/verifyCode", method=RequestMethod.GET)
    @ResponseBody
    public Result<String> getSeckillVerifyCode(HttpServletResponse response,User user,
    		@RequestParam("goodsId")long goodsId) {
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	try {
    		BufferedImage image  = seckillService.createVerifyCode(user, goodsId);
    		OutputStream out = response.getOutputStream();
    		ImageIO.write(image, "JPEG", out);
    		out.flush();
    		out.close();
    		return null;   //以流的方式已返回验证码，所以此处不需要再返回
    	}catch(Exception e) {
    		e.printStackTrace();
    		return Result.error(CodeMsg.SECKILL_FAIL);
    	}
    }
    
}
